#include "..\include\imp_typedefine.h"
#include "..\include\imp_codec_wrapper.h"
#include "..\include\imp_io_wrapper.h"
#include "..\include\imp_os_wrapper.h"

#include ".\adapter\mbm_adapter.h"
#include ".\adapter\mbm_jpeg_dev_adapter.h"

#ifdef IMP_Trace
#undef IMP_Trace
#define IMP_Trace
#endif

#ifdef __cplusplus
extern "C"
{
#endif

//#include "..\..\miicodec\bin\include\MiiTypedef.h"
//#include "..\..\miicodec\bin\include\miidec.h"
//#include "..\..\miicodec\bin\include\miienc.h"

#ifdef __cplusplus
}
#endif

#define IMP_DEC_MAX_FRAME_BUF_LEN      (150*1024)
#define IMP_DEC_MAX_CACH_FRAME_NUM      (3)
#define IMP_CACH_FRAMES_LEN    (IMP_DEC_MAX_FRAME_BUF_LEN * IMP_DEC_MAX_CACH_FRAME_NUM)

typedef struct
{
    UInt32 width;
    UInt32 height;
    UInt32 yuv_size;
	UInt8* dec_cach_frames_buf;
	UInt32 dec_cach_frames_num;
} t_imp_mp4_DS_cntx;

//extern variable

//extern function

//static or gloable variable
static t_imp_mp4_DS_cntx g_imp_mp4_DS_cntx = {0};

Int32 impcodec_miidec_open(void)
{
	IMP_Trace("impcodec_miidec_open\n");
	g_imp_mp4_DS_cntx.dec_cach_frames_buf = (UInt8*)IMP_Malloc(IMP_CACH_FRAMES_LEN + 256);
	g_imp_mp4_DS_cntx.dec_cach_frames_num = IMP_DEC_MAX_CACH_FRAME_NUM;

	if(mbm_init_decoder(g_imp_mp4_DS_cntx.dec_cach_frames_buf, 
						IMP_CACH_FRAMES_LEN, 
						g_imp_mp4_DS_cntx.dec_cach_frames_num) == IMP_False)
	{
		IMP_Free(g_imp_mp4_DS_cntx.dec_cach_frames_buf);
		IMP_Memset(&g_imp_mp4_DS_cntx, 0, sizeof(g_imp_mp4_DS_cntx));
		return 0;
	}

	IMP_Trace("imp_decode_open success");
	
	return (Int32)&g_imp_mp4_DS_cntx;
}

Int32 impcodec_miidec_decode(Int32 handle, const Int8* input, Int32 bytes, Int32 *availble)
{
	t_mbm_yuv_info yuv_info = {0};
	t_imp_mp4_DS_cntx* cntx = (t_imp_mp4_DS_cntx*)handle;

	if(mbm_decode(input, bytes, availble, &yuv_info) > 0)
	{
		IMP_Trace("imp_decode availble=%d", *availble);
	}

	cntx->width = yuv_info.width;
	cntx->height = yuv_info.height;
	cntx->yuv_size = yuv_info.yuv_size;

	return 0;
}

const void *impcodec_miidec_picture(Int32 handle,Int8* buffer)
{
	t_imp_mp4_DS_cntx* cntx = (t_imp_mp4_DS_cntx*)handle;
	UInt8* yuv_buf = NULL;
	UInt32 yuv_buf_size = 0;

	IMP_Assert(handle != 0, "null handle");
	
	yuv_buf_size = mbm_get_one_dsp_frame(&yuv_buf);

	IMP_Assert(yuv_buf != NULL, "impcodec_miidec_picture null yuv_buf_size");

	return yuv_buf;
}

void impcodec_miidec_size(Int32 handle, Int32 *width, Int32 *height,Int32* size)
{
	t_imp_mp4_DS_cntx* cntx = (t_imp_mp4_DS_cntx*)handle;

	IMP_Assert(handle != 0, "null handle");
	
	if(width != NULL)
	{
		*width = cntx->width;
	}
	if(height != NULL)
	{
		*height = cntx->height;
	}
	if(size != NULL)
	{
		*size = cntx->yuv_size;
	}
}

void impcodec_miidec_close(Int32 handle)
{
	IMP_Trace("impcodec_miidec_close\n");
	t_imp_mp4_DS_cntx* cntx = (t_imp_mp4_DS_cntx*)handle;
	
	IMP_Assert(handle != 0, "null handle");
	
	IMP_Free(cntx->dec_cach_frames_buf);
	IMP_Memset(cntx, 0, sizeof(t_imp_mp4_DS_cntx));
	
	mbm_dec_release();
}

Int32 impcodec_miienc_open(Int32 w, Int32 h, Int32 fms, Int32 bitrate)
{
	IMP_Assert(0, "impcodec_miienc_open under construct");
	return 1;
}

Int32 impcodec_miienc_encode(Int32 handle, const Int8* input, Int8* output, Int32* length, Int32* keyframe)
{
	IMP_Assert(0, "impcodec_miienc_encode under construct");
	return 1;
}
void impcodec_miienc_close(Int32 handle)
{
	IMP_Assert(0, "impcodec_miienc_close under construct");
}

extern ImpLocker g_imp_isp_locker;

#define MBM_ISP_RIGHT_TRUNK(x, bit)  ((((x) + ((1<<bit) - 1)) >> bit)<<bit)

void impmm_jpeg_decode(Int8* jpeg_data, Int32 jpeg_len, Int8 *yuv_buf, Int32 *w, Int32 *h)
{
	CriticalEnter lock(&g_imp_isp_locker);
	UInt16 w0=*w, h0=*h;
	Int32 who = w0*h0;

	Int8 *y_ptr = (Int8*)MBM_ISP_RIGHT_TRUNK((UInt32)yuv_buf, 8);
	Int8 *uv_ptr = (Int8*)MBM_ISP_RIGHT_TRUNK((UInt32)y_ptr+who,8);
	Int32 size = who*2 + 512 - ((Int32)y_ptr - (Int32)yuv_buf);
	Int32 is_yuv422;

    mbm_DecodeJpgBufYUV((UInt8*)jpeg_data, (UInt32)jpeg_len, (UInt8*)y_ptr, size, &w0, &h0, &is_yuv422);
	*w = w0; *h = h0;
	if(w0 && h0)
	{
		//YUV422->YUV420
		if(is_yuv422)
		{
			Int8 *uvo = uv_ptr;
			Int8 *uvi = uv_ptr;
			for(Int32 i=0; i<(h0>>1); i++)
			{
				IMP_Memcpy(uvo, uvi, w0);
				uvo += w0; uvi += (w0<<1);
			}
		}
	}

	IMP_Trace("impmm_jpeg_decode: 0x%x-%d | 0x%x-0x%x | %d-%d | 0x%x-%d | %d\n", yuv_buf, size, y_ptr, uv_ptr, *w, *h, jpeg_data, jpeg_len, is_yuv422);
}

//yuv -> jpeg(256 alignment)
void impmm_jpeg_encode(Int8 *yuv_buf, Int32 w, Int32 h, Int8 *out_buf, Int32 *out_len)
{
	CriticalEnter lock(&g_imp_isp_locker);
	UInt16      output_width;
	UInt16      output_height;

	UInt32 y_addr = MBM_ISP_RIGHT_TRUNK((UInt32)yuv_buf, 8);
	UInt32 uv_addr = MBM_ISP_RIGHT_TRUNK(y_addr+w*h, 8);

	ImpBool value = mbm_EncodeJpgBuf(y_addr, uv_addr, w, h, 
                                     &output_width, &output_height,
                                     (UInt8*)out_buf, (UInt32*)out_len);
	IMP_Trace("impmm_jpeg_encode: 0x%x-0x%x | %d-%d | %d-%d | 0x%x-%d\n", y_addr, uv_addr, w, h, output_width, output_height, out_buf, *out_len);
    IMP_Assert(value == IMP_True, "impmm_jpeg_encode(0)");
}

